#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <linux/errno.h>
//#include <linux/wait.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/syscall_barrier.h>

extern spinlock_t g_lock;
extern list_head g_blist;

asmlinkage long sys_barrierwait(int barrierID) {
	//barrier_list_t g_blist;
	//must verify that the barrier ID is valid
	//search for barrier id in list and retrieve barrier structure
	barrier_data_t *t_bl;
	struct list_head *pos;
	
	printk(KERN_ALERT "In wait:..starting\n");

	//lock g_blistspin_unlock(&g_blist.list_lock);
	spin_lock(&g_lock);
	
	//search g_blist for barrier
	list_for_each(pos, &g_blist) {
		t_bl = list_entry(pos, barrier_data_t, b_list_elem);
		spin_lock(&(t_bl->barrier_lock));//lock barrier
		//int t_rc = t_bl->ref_count;
		printk(KERN_ALERT "In wait: passed barrierID = %d\n", barrierID);
		printk(KERN_ALERT "In wait: t_bl barrierID = %d\n", (int)atomic_read(&t_bl->ID));
		if(atomic_read(&t_bl->ID) == barrierID) {
		  printk(KERN_ALERT "In wait: ref_count = %d\n", atomic_read(&t_bl->ref_count));
		  printk(KERN_ALERT "In wait: N = %d\n", atomic_read(&t_bl->N));
			atomic_inc(&t_bl->ref_count);
			printk(KERN_ALERT "In wait: ref_count = %d\n", atomic_read(&t_bl->ref_count));
				if(atomic_read(&t_bl->ref_count) > atomic_read(&t_bl->N))
				{
				  
					spin_unlock(&(t_bl->barrier_lock));
					spin_unlock(&g_lock);
					kfree(t_bl);
					return -1;
				}
				wait_queue_t q;
				init_waitqueue_entry(&q,current);
				printk(KERN_ALERT "before prep_to_wait..\n");
				prepare_to_wait(&(t_bl->wq_head), &q, TASK_INTERRUPTIBLE);
				
				//get current process id
				//assume that wait queue for barrier is not full
				//add current to wait queue
				
				if(atomic_read(&t_bl->ref_count) == atomic_read(&t_bl->N))
				{
				  printk(KERN_ALERT "In Wait: ref count == N\n");
					//this process was last to join so...
					//call syscall_barrier_clear internal function
					wake_up_all(&(t_bl->wq_head));
					
					//XXX-Iterate thru queue
					//finish_wait
										
					//clear wait queue	
					atomic_set(&t_bl->ref_count, 0);
					spin_unlock(&(t_bl->barrier_lock));
					spin_unlock(&g_lock);
					kfree(t_bl);
					return 0;
				}
				
				
			spin_unlock(&(t_bl->barrier_lock));
			spin_unlock(&g_lock);
			kfree(t_bl);
			return 0;
		}
		spin_unlock(&(t_bl->barrier_lock));//release barrier_lock
		if (pos->next == &g_blist) {
			spin_unlock(&g_lock);
			kfree(t_bl);
			return -1;
		}
	}
	spin_unlock(&g_lock);//release g_blist
	//	kfree(t_bl);
	return -1;
	

	//The barrier being destroyed while a process waits is also a failure condition that should return -1	



}
